﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using Defarm.Helpers;
using Defarm.Model;
using Defarm.Model.Common;
using Defarm.Model.DB;
using Defarm.View;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

namespace Defarm.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        #region Fields

        #region Db

        private DefarmDbContext db;

        private ObservableCollection<Position> positions;

        private ObservableCollection<Position> positionsInBase;

        #endregion

        #region Common

        private Position selectedPosition;

        private Position selectedPositionTemp;

        private PositionWithPrice selectedPositionWithPriceTemp;

        #endregion

        #region AssortWindow

        private RelayCommand addPositionFromBase;

        private RelayCommand deletePosition;

        private RelayCommand showAssortWindow;

        #endregion

        #region SaleWindow

        private ObservableCollection<PositionWithPrice> positionsInSale;

        private RelayCommand addPositionToSale;

        private RelayCommand deletePositionFromSale;

        private RelayCommand conductSale;

        private RelayCommand showSaleWindow;

        #endregion
        
        #region OrderWindow

        private ObservableCollection<PositionWithPrice> positionsInOrder;

        private RelayCommand addPositionToOrder;

        private RelayCommand deletePositionFromOrder;

        private RelayCommand conductOrder;

        private RelayCommand showOrderWindow;

        #endregion
        
        #region OutcomeWindow

        private ObservableCollection<Position> positionsInOutcome;

        private RelayCommand addPositionToOutcome;

        private RelayCommand deletePositionFromOutcome;

        private RelayCommand conductOutcome;

        private RelayCommand showOutcomeWindow;

        #endregion
        
        #region IncomeWindow

        private ObservableCollection<Order> unrealOrders;

        private ObservableCollection<PositionWithRealesedQuantity> positionsInIncome;

        private Order selectedOrder;

        private RelayCommand conductIncome;

        private RelayCommand showIncomeWindow;

        #endregion
            
        #endregion

        #region Constructors

        public MainViewModel()
        {
            this.Db = new DefarmDbContext();
            this.Positions = new ObservableCollection<Position>();
            this.PositionsInBase = new ObservableCollection<Position>();
            this.PositionsInSale = new ObservableCollection<PositionWithPrice>();
            this.PositionsInOrder = new ObservableCollection<PositionWithPrice>();
            this.PositionsInOutcome = new ObservableCollection<Position>();
            this.PositionsInIncome = new ObservableCollection<PositionWithRealesedQuantity>();
            this.UnrealOrders = new ObservableCollection<Order>();

            var loginWindow = new LoginWindow();
            ViewModelLocator.Login.LoginWindow = loginWindow;
            loginWindow.ShowDialog();

            //this.ClearDb();

            this.InitPositions();
            this.InitUnrealOrders();

            var temp = DbHelper.GetPositionsFromBase();
            foreach (var position in temp)
            {
                this.PositionsInBase.Add(position);
            }
        }

        #endregion

        #region Public properties

        #region Db

        public DefarmDbContext Db
        {
            get
            {
                return this.db;
            }

            set
            {
                this.db = value;
                this.RaisePropertyChanged(() => this.Db);
            }
        }

        public ObservableCollection<Position> Positions
        {
            get
            {
                return this.positions;
            }

            set
            {
                this.positions = value;
                this.RaisePropertyChanged(() => this.Positions);
            }
        }

        public ObservableCollection<Position> PositionsInBase
        {
            get
            {
                return this.positionsInBase;
            }

            set
            {
                this.positionsInBase = value;
                this.RaisePropertyChanged(() => this.PositionsInBase);
            }
        }

        #endregion

        #region Common

        public Position SelectedPosition
        {
            get
            {
                return this.selectedPosition;
            }

            set
            {
                this.selectedPosition = value;
                this.RaisePropertyChanged(() => this.SelectedPosition);
            }
        }

        public Position SelectedPositionTemp
        {
            get
            {
                return this.selectedPositionTemp;
            }

            set
            {
                this.selectedPositionTemp = value;
                this.RaisePropertyChanged(() => this.SelectedPositionTemp);
            }
        }

        public PositionWithPrice SelectedPositionWithPriceTemp
        {
            get
            {
                return this.selectedPositionWithPriceTemp;
            }

            set
            {
                this.selectedPositionWithPriceTemp = value;
                this.RaisePropertyChanged(() => this.SelectedPositionWithPriceTemp);
            }
        }

        #endregion

        #region AssortWindow

        public RelayCommand AddPositionFromBase
        {
            get
            {
                if (this.addPositionFromBase == null)
                {
                    this.addPositionFromBase = new RelayCommand(() =>
                    {
                        if (this.SelectedPositionTemp != null)
                        {
                            bool check = true;
                            foreach (var position in this.Positions)
                            {
                                if (position.Id == this.SelectedPositionTemp.Id)
                                {
                                    check = false;
                                }
                            }
                            if (check)
                            {
                                var position = new Position
                                {
                                    Id = this.SelectedPositionTemp.Id,
                                    Name = this.SelectedPositionTemp.Name,
                                    Quantity = 0,
                                    OrderMaps = new ObservableCollection<OrderMap>(),
                                    OutcomeMaps = new ObservableCollection<OutcomeMap>(),
                                    SaleMaps = new ObservableCollection<SaleMap>()
                                };
                                this.Positions.Add(position);
                                this.Db.Positions.Add(position);
                                this.Db.SaveChanges();
                            }
                        }
                    });
                }

                return this.addPositionFromBase;
            }
        }

        public RelayCommand DeletePosition
        {
            get
            {
                if (this.deletePosition == null)
                {
                    this.deletePosition = new RelayCommand(() =>
                    {
                        if (this.SelectedPosition != null)
                        {
                            this.Db.Positions.Remove(this.Db.Positions.Find(this.SelectedPosition.Id));
                            this.Db.SaveChanges();
                            this.Positions.Remove(this.Positions.FirstOrDefault(p => p.Id == this.SelectedPosition.Id));
                        }
                    });
                }

                return this.deletePosition;
            }
        }

        public RelayCommand ShowAssortWindow
        {
            get
            {
                if (this.showAssortWindow == null)
                {
                    this.showAssortWindow = new RelayCommand(() =>
                    {
                        var assortWindow = new AssortWindow();
                        this.SelectedPosition = null;
                        this.SelectedPositionTemp = null;
                        assortWindow.ShowDialog();
                    });
                }

                return this.showAssortWindow;
            }
        }

        #endregion
        
        #region SaleWindow

        public ObservableCollection<PositionWithPrice> PositionsInSale
        {
            get
            {
                return this.positionsInSale;
            }

            set
            {
                this.positionsInSale = value;
                this.RaisePropertyChanged(() => this.PositionsInSale);
            }
        }

        public RelayCommand AddPositionToSale
        {
            get
            {
                if (this.addPositionToSale == null)
                {
                    this.addPositionToSale = new RelayCommand(() =>
                    {
                        this.AddPositionToList(this.PositionsInSale, this.SelectedPosition);
                    });
                }

                return this.addPositionToSale;
            }
        }

        public RelayCommand DeletePositionFromSale
        {
            get
            {
                if (this.deletePositionFromSale == null)
                {
                    this.deletePositionFromSale = new RelayCommand(() =>
                    {
                        if (this.SelectedPositionWithPriceTemp != null)
                        {
                            this.PositionsInSale.Remove(this.SelectedPositionWithPriceTemp);
                        }
                    });
                }

                return this.deletePositionFromSale;
            }
        }

        public RelayCommand ConductSale
        {
            get
            {
                if (this.conductSale == null)
                {
                    this.conductSale = new RelayCommand(() =>
                    {
                        bool checkPrices = true;
                        bool checkQuantity = true;
                        foreach (var positionWithPrice in this.PositionsInSale)
                        {
                            if (positionWithPrice.Price <= 0)
                            {
                                checkPrices = false;
                                break;
                            }
                        }
                        foreach (var positionWithPrice in this.PositionsInSale)
                        {
                            foreach (var position in this.Positions)
                            {
                                if (positionWithPrice.Position.Id == position.Id &&
                                    positionWithPrice.Quantity > position.Quantity)
                                {
                                    checkQuantity = false;
                                }
                            }
                        }
                        if (checkPrices && checkQuantity && this.PositionsInSale.Count > 0)
                        {
                            var sale = new Sale
                            {
                                Id = Guid.NewGuid(),
                                Date = DateTime.Now.ToString(),
                                SaleMaps = new ObservableCollection<SaleMap>()
                            };

                            foreach (var positionWithPrice in this.PositionsInSale)
                            {
                                foreach (var position in this.Db.Positions)
                                {
                                    if (position.Id == positionWithPrice.Position.Id)
                                    {
                                        position.Quantity -= positionWithPrice.Quantity;
                                        this.Db.SaleMaps.Add(new SaleMap
                                        {
                                            Id = Guid.NewGuid(),
                                            Position = position,
                                            Sale = sale,
                                            Price = positionWithPrice.Price,
                                            Quantity = positionWithPrice.Quantity
                                        });
                                    }
                                }
                            }

                            this.Db.Sales.Add(sale);
                            this.Db.SaveChanges();
                            this.InitPositions();
                            this.PositionsInSale.Clear();
                        }
                        else
                        {
                            if (!checkPrices)
                            {
                                MessageBox.Show("There are positions with wrong prices!", "Wrong prices",
                                                             MessageBoxButton.OK, MessageBoxImage.Warning);
                            }
                            if (!checkQuantity)
                            {
                                MessageBox.Show("There are positions with wrong quantities!", "Wrong quantities",
                                                             MessageBoxButton.OK, MessageBoxImage.Warning);
                            }
                        }

                    });
                }

                return this.conductSale;
            }
        }

        public RelayCommand ShowSaleWindow
        {
            get
            {
                if (this.showSaleWindow == null)
                {
                    this.showSaleWindow = new RelayCommand(() =>
                    {
                        var saleWindow = new SaleWindow();
                        this.SelectedPosition = null;
                        this.SelectedPositionWithPriceTemp = null;
                        saleWindow.ShowDialog();
                    });
                }

                return this.showSaleWindow;
            }
        }

        #endregion
        
        #region OrderWindow

        public ObservableCollection<PositionWithPrice> PositionsInOrder
        {
            get
            {
                return this.positionsInOrder;
            }

            set
            {
                this.positionsInOrder = value;
                this.RaisePropertyChanged(() => this.PositionsInOrder);
            }
        }

        public RelayCommand AddPositionToOrder
        {
            get
            {
                if (this.addPositionToOrder == null)
                {
                    this.addPositionToOrder = new RelayCommand(() =>
                    {
                        this.AddPositionToList(this.PositionsInOrder, this.SelectedPosition);
                    });
                }

                return this.addPositionToOrder;
            }
        }

        public RelayCommand DeletePositionFromOrder
        {
            get
            {
                if (this.deletePositionFromOrder == null)
                {
                    this.deletePositionFromOrder = new RelayCommand(() =>
                    {
                        if (this.SelectedPositionWithPriceTemp != null)
                        {
                            this.PositionsInOrder.Remove(this.SelectedPositionWithPriceTemp);
                        }
                    });
                }

                return this.deletePositionFromOrder;
            }
        }

        public RelayCommand ConductOrder
        {
            get
            {
                if (this.conductOrder == null)
                {
                    this.conductOrder = new RelayCommand(() =>
                    {
                        bool checkQuantity = true;
                        bool checkPrices = true;
                        foreach (var position in this.PositionsInOrder)
                        {
                            if (position.Price <= 0)
                            {
                                checkPrices = false;
                            }

                            if (position.Quantity <= 0)
                            {
                                checkQuantity = false;
                            }
                        }
                        if (checkPrices && checkQuantity && this.PositionsInOrder.Count > 0)
                        {
                            var order = new Order
                            {
                                Id = Guid.NewGuid(),
                                Date = DateTime.Now.ToString(),
                                IsRealesed = false,
                                OrderMaps = new ObservableCollection<OrderMap>()
                            };

                            foreach (var positionInOrder in this.PositionsInOrder)
                            {
                                foreach (var position in this.Db.Positions)
                                {
                                    if (positionInOrder.Position.Id == position.Id)
                                    {
                                        this.Db.OrderMaps.Add(new OrderMap
                                        {
                                            Id = Guid.NewGuid(),
                                            Position = position,
                                            Order = order,
                                            OrderQuantity = positionInOrder.Quantity,
                                            Price = positionInOrder.Price,
                                            RealesedQuantity = 0
                                        });
                                    }
                                }
                            }
                            this.Db.Orders.Add(order);
                            this.Db.SaveChanges();
                            this.InitPositions();
                            this.PositionsInOrder.Clear();
                        }
                        else
                        {
                            if (!checkPrices)
                            {
                                MessageBox.Show("There are positions with wrong prices!", "Wrong prices",
                                                             MessageBoxButton.OK, MessageBoxImage.Warning);
                            }
                            if (!checkQuantity)
                            {
                                MessageBox.Show("There are positions with wrong quantities!", "Wrong quantities",
                                                             MessageBoxButton.OK, MessageBoxImage.Warning);
                            }
                        }

                    });
                }

                return this.conductOrder;
            }
        }

        public RelayCommand ShowOrderWindow
        {
            get
            {
                if (this.showOrderWindow == null)
                {
                    this.showOrderWindow = new RelayCommand(() =>
                    {
                        var orderWindow = new OrderWindow();
                        this.SelectedPosition = null;
                        this.SelectedPositionWithPriceTemp = null;
                        orderWindow.ShowDialog();
                    });
                }

                return this.showOrderWindow;
            }
        }

        #endregion
        
        #region OutcomeWindow

        public ObservableCollection<Position> PositionsInOutcome
        {
            get
            {
                return this.positionsInOutcome;
            }

            set
            {
                this.positionsInOutcome = value;
                this.RaisePropertyChanged(() => this.PositionsInOutcome);
            }
        }

        public RelayCommand AddPositionToOutcome
        {
            get
            {
                if (this.addPositionToOutcome == null)
                {
                    this.addPositionToOutcome = new RelayCommand(() =>
                    {
                        this.AddPositionToList(this.PositionsInOutcome, this.SelectedPosition);
                    });
                }

                return this.addPositionToOutcome;
            }
        }

        public RelayCommand DeletePositionFromOutcome
        {
            get
            {
                if (this.deletePositionFromOutcome == null)
                {
                    if (this.SelectedPositionTemp != null)
                    {
                        this.PositionsInOutcome.Remove(this.SelectedPositionTemp);
                    }
                }

                return this.deletePositionFromOutcome;
            }
        }

        public RelayCommand ConductOutcome
        {
            get
            {
                if (this.conductOutcome == null)
                {
                    this.conductOutcome = new RelayCommand(() =>
                    {
                        bool checkQuantity = true;
                        foreach (var positionInOutcome in this.PositionsInOutcome)
                        {
                            foreach (var position in this.Positions)
                            {
                                if (positionInOutcome.Id == position.Id &&
                                    (positionInOutcome.Quantity > position.Quantity ||
                                     positionInOutcome.Quantity < 0))
                                {
                                    checkQuantity = false;
                                }
                            }
                        }
                        if (checkQuantity && this.PositionsInOutcome.Count > 0)
                        {
                            var outcome = new Outcome
                            {
                                Id = Guid.NewGuid(),
                                Date = DateTime.Now.ToString(),
                                OutcomeMaps = new ObservableCollection<OutcomeMap>()
                            };

                            foreach (var positionInOutcome in this.PositionsInOutcome)
                            {
                                foreach (var position in this.Db.Positions)
                                {
                                    if (positionInOutcome.Id == position.Id)
                                    {
                                        position.Quantity -= positionInOutcome.Quantity;
                                        this.Db.OutcomeMaps.Add(new OutcomeMap
                                        {
                                            Id = Guid.NewGuid(),
                                            Position = position,
                                            Quantity = positionInOutcome.Quantity,
                                            Outcome = outcome
                                        });
                                    }
                                }
                            }

                            this.Db.Outcomes.Add(outcome);
                            this.Db.SaveChanges();
                            this.InitPositions();
                            this.PositionsInOutcome.Clear();
                        }
                        else
                        {
                            MessageBox.Show("There are positions with wrong quantities!", "Wrong quantities",
                                                             MessageBoxButton.OK, MessageBoxImage.Warning);
                        }
                    });
                }

                return this.conductOutcome;
            }
        }

        public RelayCommand ShowOutcomeWindow
        {
            get
            {
                if (this.showOutcomeWindow == null)
                {
                    this.showOutcomeWindow = new RelayCommand(() =>
                    {
                        var outcomeWindow = new OutcomeWindow();
                        this.SelectedPosition = null;
                        this.SelectedPositionTemp = null;
                        outcomeWindow.ShowDialog();
                    });
                }

                return this.showOutcomeWindow;
            }
        }

        #endregion
        
        #region IncomeWindow

        public ObservableCollection<Order> UnrealOrders
        {
            get
            {
                return this.unrealOrders;
            }

            set
            {
                this.unrealOrders = value;
                this.RaisePropertyChanged(() => this.UnrealOrders);
            }
        }

        public ObservableCollection<PositionWithRealesedQuantity> PositionsInIncome
        {
            get
            {
                return this.positionsInIncome;
            }

            set
            {
                this.positionsInIncome = value;
                this.RaisePropertyChanged(() => this.PositionsInIncome);
            }
        }

        public Order SelectedOrder
        {
            get
            {
                return this.selectedOrder;
            }

            set
            {
                this.selectedOrder = value;

                this.PositionsInIncome.Clear();
                if (value != null)
                {
                    var selected = this.Db.Orders.Find(value.Id);
                    foreach (var orderMap in selected.OrderMaps)
                    {
                        this.PositionsInIncome.Add(new PositionWithRealesedQuantity
                        {
                            OrderQuantity = orderMap.OrderQuantity,
                            ReleasedQuantity = 0,
                            Position = orderMap.Position
                        });
                    }
                }
 
                this.RaisePropertyChanged(() => this.SelectedOrder);
            }
        }

        public RelayCommand ConductIncome
        {
            get
            {
                if (this.conductIncome == null)
                {
                    this.conductIncome = new RelayCommand(() =>
                    {
                        if (this.PositionsInIncome.Count > 0 && this.SelectedOrder != null)
                        {
                            bool checkReQuantity = true;
                            foreach (var position in this.PositionsInIncome)
                            {
                                if (position.ReleasedQuantity > position.OrderQuantity)
                                {
                                    checkReQuantity = false;
                                    break;
                                }
                            }

                            if (checkReQuantity)
                            {
                                this.Db.Orders.FirstOrDefault(o => o.Id == this.SelectedOrder.Id).IsRealesed = true;

                                foreach (var positionInIncome in this.PositionsInIncome)
                                {
                                    foreach (var position in this.Db.Positions)
                                    {
                                        if (positionInIncome.Position.Id == position.Id)
                                        {
                                            position.Quantity += positionInIncome.ReleasedQuantity;
                                        }
                                    }
                                }

                                this.Db.SaveChanges();
                                this.InitPositions();
                                this.InitUnrealOrders();
                                this.PositionsInIncome.Clear();
                                this.SelectedOrder = null; 
                            }
                            else
                            {
                                MessageBox.Show("There are positions with wrong quantities!", "Wrong quantities",
                                                            MessageBoxButton.OK, MessageBoxImage.Warning);
                            }
                        }
                    });
                }

                return this.conductIncome;
            }
        }

        public RelayCommand ShowIncomeWindow
        {
            get
            {
                if (this.showIncomeWindow == null)
                {
                    this.showIncomeWindow = new RelayCommand(() =>
                    {
                        var incomeWindow = new IncomeWindow();
                        this.InitUnrealOrders();
                        incomeWindow.ShowDialog();
                    });
                }

                return this.showIncomeWindow;
            }
        }

        #endregion
        
        #endregion

        #region Private methods

        private bool AddPositionToList(ObservableCollection<PositionWithPrice> list, Position selected)
        {
            if (selected != null)
            {
                bool check = true;
                foreach (var position in list)
                {
                    if (position.Position.Id == selected.Id)
                    {
                        check = false;
                    }
                }
                if (check)
                {
                    list.Add(new PositionWithPrice
                    {
                        Position = new Position
                        {
                            Id = selected.Id,
                            Name = selected.Name,
                            Quantity = 0
                        },
                        Price = 0
                    });
                }

                return true;
            }

            return false;
        }

        private bool AddPositionToList(ObservableCollection<Position> list, Position selected)
        {
            if (selected != null)
            {
                bool check = true;
                foreach (var position in list)
                {
                    if (position.Id == selected.Id)
                    {
                        check = false;
                    }
                }
                if (check)
                {
                    list.Add(new Position
                    {
                        Id = selected.Id,
                        Name = selected.Name,
                        Quantity = 0
                    });
                }

                return true;
            }

            return false;
        }

        private void ClearDb()
        {
            foreach (var position in this.Db.Positions)
            {
                position.OrderMaps.Clear();
                position.OutcomeMaps.Clear();
                position.SaleMaps.Clear();
            }

            foreach (var sale in this.Db.Sales)
            {
                sale.SaleMaps.Clear();
            }

            foreach (var order in this.Db.Orders)
            {
                order.OrderMaps.Clear();
            }

            foreach (var outcome in this.Db.Outcomes)
            {
                outcome.OutcomeMaps.Clear();
            }

            /*foreach (var saleMap in this.Db.SaleMaps)
            {
                saleMap.Position = null;
                saleMap.Sale = null;
            }

            foreach (var orderMap in this.Db.OrderMaps)
            {
                orderMap.Position = null;
                orderMap.Order = null;
            }

            foreach (var outcomeMap in this.Db.OutcomeMaps)
            {
                outcomeMap.Position = null;
                outcomeMap.Outcome = null;
            }*/

            var tempPos = this.Db.Positions.ToList();
            this.Db.Positions.RemoveRange(tempPos);

            var tempSale = this.Db.Sales.ToList();
            this.Db.Sales.RemoveRange(tempSale);

            var tempOrder = this.Db.Orders.ToList();
            this.Db.Orders.RemoveRange(tempOrder);

            var tempOutcome = this.Db.Outcomes.ToList();
            this.Db.Outcomes.RemoveRange(tempOutcome);

            var tempSMap = this.Db.SaleMaps.ToList();
            this.Db.SaleMaps.RemoveRange(tempSMap);

            var tempOMap = this.Db.OrderMaps.ToList();
            this.Db.OrderMaps.RemoveRange(tempOMap);

            var tempOtMap = this.Db.OutcomeMaps.ToList();
            this.Db.OutcomeMaps.RemoveRange(tempOtMap);

            this.Db.SaveChanges();
        }

        private void InitPositions()
        {
            this.Positions.Clear();
            foreach (var position in this.Db.Positions)
            {
                this.Positions.Add(new Position
                {
                    Id = position.Id,
                    Name = position.Name,
                    Quantity = position.Quantity,
                    OrderMaps = position.OrderMaps,
                    OutcomeMaps = position.OutcomeMaps,
                    SaleMaps = position.SaleMaps
                });
            }
        }

        private void InitUnrealOrders()
        {
            this.UnrealOrders.Clear();
            foreach (var order in this.Db.Orders)
            {
                if (!order.IsRealesed)
                {
                    this.UnrealOrders.Add(order);
                }
            }
        }

        #endregion
    }
}